#include "message_bond_impl.h"

#include <core/Utility.h>
#include <core/sharedata/login_data.h>
#include <qbnet/source/network_service_impl.h>
#include <qbprotocol/include/RequestNetWrapper.h>
#include <qbprotocol/include/SSMsgStruct.h>
#include <qbtools/message/MessagePoster.h>

#include "../message_kernel_impl.h"

#ifndef WIN32
#include <arpa/inet.h>
#else
#pragma comment(lib, "ws2_32.lib")
#endif

namespace qb {
CMessageBondImpl::CMessageBondImpl()
    : m_spNetworkService(NULL),
      m_spMessageKernel(NULL),
      m_spMsgProcess(nullptr) {}

CMessageBondImpl::~CMessageBondImpl() {}

STDMETHODIMP_(void)
CMessageBondImpl::OnResponse(THIS_ int32_t msgtype, qb::ProcessMessage msp) {
  assert(msgtype == E_FID_SERVER_QPID || msgtype == E_FID_SERVER_QPID_JAVA);
  if (msp.getBuff() == nullptr || msp.getLen() <= 0) return;

  int iFunid = 0;

  xMsgHeadAck_t xHead;
  memcpy(&xHead, msp.getBuff(), sizeof(xHead));
  xHead.msgtype = ntohl(xHead.msgtype);
  xHead.retcode = ntohs(xHead.retcode);
  xHead.idx = ntohs(xHead.idx);
  xHead.num = ntohs(xHead.num);

  m_Paser.Clean();

  SSAckMsg stAck;
  iFunid = m_Paser.GetMessageType(msp.getBuff() + sizeof(xHead),
                                  msp.getLen() - sizeof(xHead), stAck);
  stAck.m_FuncID = iFunid;

  DecodeFunc df = m_msgDecodeFuncs[iFunid];
  if (df) {
    m_Paser.SetDecodeFunc(df);
    stAck.m_pRet = m_Paser.DecodeMessage(&stAck);
    if (stAck.m_pRet == nullptr) log_warning("Message Decode NULL: %d", iFunid);

    if (iFunid != 50129 && iFunid != 50903 && iFunid != 50901 &&
        iFunid != 50906)
      log_info("Receive message %d, requestid:%d, %d/%d, len:%d", iFunid,
               stAck.m_RequestID, stAck.m_Index, stAck.m_Count, msp.getLen());
  } else {
    log_warning("Message No Decoder: %d", iFunid);
  }

  {
    Locker lock(m_lock);
    auto iters = m_msgProcessors.equal_range(iFunid);
    for (auto iter = iters.first; iter != iters.second; ++iter) {
      iter->second->OnResponse(iFunid, stAck);
    }
  }
  if (m_specificMsgProcesser) m_specificMsgProcesser->OnResponse(iFunid, stAck);

  // 清理数据
  if (df && stAck.m_pRet) {
    static ProtoMessage s_dumy_msg;
    void* p = df(stAck.m_FuncID, s_dumy_msg, stAck.m_pRet);
    assert(p == nullptr);
  }
}

STDMETHODIMP_(bool)
CMessageBondImpl::UnRegisterMsgProcessor(THIS_ int funcId,
                                         IQpidMessageProcessor* p) {
  if (p == nullptr) return false;
  Locker lock(m_lock);
  auto iters = m_msgProcessors.equal_range(funcId);
  for (auto iter = iters.first; iter != iters.second; ++iter) {
    if (iter->second == p) {
      m_msgProcessors.erase(iter);
      break;
    }
  }

  return true;
}

STDMETHODIMP_(bool)
CMessageBondImpl::RegisterMsgProcessor(THIS_ int funcId,
                                       IQpidMessageProcessor* p) {
  if (p == nullptr) return false;

  Locker lock(m_lock);
  m_msgProcessors.insert({funcId, p});
  return true;
}

STDMETHODIMP_(bool) CMessageBondImpl::RegisterDecodeFunc(int v, DecodeFunc f) {
  if (f == nullptr) return false;
  m_msgDecodeFuncs[v] = f;
  return false;
}

STDMETHODIMP_(int32_t)
CMessageBondImpl::Create(THIS_ INetworkService* ins, IMessageKernel* kernel,
                         IMessageProcess* process) {
  m_spNetworkService = ins;
  m_spMessageKernel = kernel;
  m_spMsgProcess = process;
  AttachModules();

  m_networkHeartBeat.Create();
  return 0;
}

STDMETHODIMP_(int32_t) CMessageBondImpl::Destory(THIS_) {
  m_networkHeartBeat.Destroy();
  DetachModules();
  return 0;
}

int32_t CMessageBondImpl::RequestHeartBeat() {
  // TODO: 移走到AUTH模块
  xMsgHeadReq_t xhead;
  xhead.msgtype = htonl(E_FID_CS_HEARTBEAT);

  int off = 0;
  char sndBuf[Y_RD_SERVICE_MESSAGE_BLOCK_LEN];
  memset(sndBuf, 0x00, Y_RD_SERVICE_MESSAGE_BLOCK_LEN);
  rdMessage::bPack((char*)&xhead, sizeof(xhead), (char*)nullptr, 0, sndBuf,
                   &off);

  return m_spMessageKernel->Send(qb::loginData::instance().ConnectID(), sndBuf,
                                 off);
}

STDMETHODIMP_(void)
CMessageBondImpl::SetSpecificMsgProcessor(IQpidMessageProcessor* p) {
  m_specificMsgProcesser = p;
}

void CMessageBondImpl::OnHeartBeat() {
  if (eNetStatus_Done != loginData::instance().GetCurrentStatus()) {
    return;
  }

  RequestHeartBeat();
}

void CMessageBondImpl::AttachModules() {
  m_networkHeartBeat.RegisterNotify(this);
  if (m_spMsgProcess) {
    m_spMsgProcess->RegisterMsgProcessorOnThread(E_FID_SERVER_QPID, this);
    m_spMsgProcess->RegisterMsgProcessorOnThread(E_FID_SERVER_QPID_JAVA, this);
  }
}

void CMessageBondImpl::DetachModules() {
  if (m_spMsgProcess) {
    m_spMsgProcess->UnRegisterMsgProcessorOnThread(E_FID_SERVER_QPID, this);
    m_spMsgProcess->UnRegisterMsgProcessorOnThread(E_FID_SERVER_QPID_JAVA,
                                                   this);
  }

  m_networkHeartBeat.DeRegisterNotify(this);

  m_spNetworkService = NULL;
}
}  // namespace qb
